package com.example.demo.dao.impl;

import java.io.Serializable;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.ArrayUtils;
import org.hibernate.HibernateException;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Projections;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.support.DataAccessUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

import com.example.demo.dao.CriteriaUtils;
import com.example.demo.dao.Dao;
import com.example.demo.dao.HibernateLimitationHandler;
import com.example.demo.dao.HibernatePaginationHandler;
import com.example.demo.dao.PageRequestWrapper;


@SuppressWarnings({"deprecation"})
public abstract class DaoSupport<T> extends HibernateDaoSupport implements Dao<T> {

    private static final Logger logger = LoggerFactory.getLogger(DaoSupport.class);

    private static final int DEFAULT_BATCH_INSERT_SIZE = 50;
    
    
    @Autowired
    void setSuperSessionFactory(SessionFactory sessionFactory) {
        super.setSessionFactory(sessionFactory);
    }
    

    /**
     * 根据Id查询指定对象
     * @param id 对象Id
     * @return 指定对象
     */
    public T findById(Serializable id) {
    	T entity = getHibernateTemplate().get(getEntityClass(), id);
    	if(entity != null){
    		getHibernateTemplate().evict(entity);
    	}
    	
        return entity;
    }

    protected T findFirst(String hql, Map<String, ?> params) {
        List<T> list = queryByHql(hql, params);
        if(list.isEmpty()) {
            return null;
        }
        return list.get(0);
    }

    protected T findFirst(String hql, Object... params) {
        List<T> list = queryByHql(hql, params);
        if(list.isEmpty()) {
            return null;
        }
        return list.get(0);
    }

    @SuppressWarnings("unchecked")
    protected T findSingle(String hql, Object... params) {
        List<T> list = (List<T>) getHibernateTemplate().find(hql, params);
        return DataAccessUtils.singleResult(list);
    }

    protected T findSingle(String hql, Map<String, ?> params) {
        List<T> list = queryByHql(hql, params);
        return DataAccessUtils.singleResult(list);
    }

    /**
     * 根据指定的Hql语句进行数据库查询
     * @param hql hql语句
     * @return 对象集合
     */
    @SuppressWarnings("unchecked")
    protected List<T> queryByHql(String hql) {
        return (List<T>) getHibernateTemplate().find(hql);
    }

    @SuppressWarnings("unchecked")
    protected List<T> queryByHql(String hql, Object... params) {
        return (List<T>) getHibernateTemplate().find(hql, params);
    }

    /**
     * 根据指定的Hql语句进行数据库查询
     * @param hql hql语句
     * @param params 查询参数
     * @return 对象集合
     */
    @SuppressWarnings("unchecked")
    protected List<T> queryByHql(String hql, Map<String, ?> params) {
        NamedParameterPair namedParameterPair = new NamedParameterPair(params);
        return (List<T>) getHibernateTemplate().findByNamedParam(hql,
                namedParameterPair.getParamNames(), namedParameterPair.getValues());
    }

    /**
     * 根据指定的Hql语句进行数据库查询，只返回指定的行数
     * @param hql hql语句
     * @param params 查询参数
     * @param offset 从指定行数开始取得数据，从0开始
     * @param rowCount 返回的行数
     * @return 对象集合
     */
    protected List<T> queryByHql(String hql, Map<String, ?> params, int offset, int rowCount) {
        return getHibernateTemplate().execute(new HibernateLimitationHandler<List<T>>(hql, params, offset, rowCount));
    }

    /**
     * 根据指定的criteria进行数据库查询
     * @param criteria criteria查询条件
     * @return 对象集合
     */
    @SuppressWarnings("unchecked")
    protected List<T> queryByCriteria(DetachedCriteria criteria) {
        return (List<T>)getHibernateTemplate().findByCriteria(criteria);
    }

    /**
     * 根据指定的criteria进行数据库分页查询
     * @param criteria criteria查询条件
     * @param pageNum 当前页
     * @param pageSize 每页记录数
     * @return 对象集合（分页）
     */
    protected Page<T> queryByCriteria(DetachedCriteria criteria, int pageNum,
                                      int pageSize) {
        long total = count(criteria);
        return queryByCriteria(criteria, pageNum, pageSize, total);
    }

    /**
     * 根据指定的criteria进行数据库分页查询
     * @param criteria criteria查询条件
     * @param pageNum 当前页
     * @param pageSize 每页记录数
     * @param total 总记录数
     * @return 对象集合（分页）
     */
    @SuppressWarnings("unchecked")
    protected Page<T> queryByCriteria(DetachedCriteria criteria, int pageNum,
                                      int pageSize, long total) {
        PageRequestWrapper pageRequestWrapper = new PageRequestWrapper(pageNum, pageSize);
        List<T> content = (List<T>)getHibernateTemplate().findByCriteria(criteria,
                pageRequestWrapper.getOffset(),
                pageRequestWrapper.getPageSize());
        return new PageImpl<T>(content, pageRequestWrapper, total);
    }

    /**
     * 根据指定的Hql语句统计数据行数
     * @param queryString hql语句
     * @return 数据行数
     */
    protected long count(String queryString) {
        return DataAccessUtils.longResult(getHibernateTemplate().find(queryString));
    }

    /**
     * 根据指定的Hql语句统计数据行数
     * @param queryString hql语句
     * @param params 查询参数
     * @return 数据行数
     */
    protected long count(String queryString, Map<String, ?> params) {
        NamedParameterPair namedParameterPair = new NamedParameterPair(params);
        List result = getHibernateTemplate().findByNamedParam(queryString,
                namedParameterPair.getParamNames(), namedParameterPair.getValues());
        return DataAccessUtils.longResult(result);
    }

    /**
     * 根据指定的criteria统计记录行数
     * @param criteria 查询条件
     * @return 记录行数
     */
    protected long count(DetachedCriteria criteria) {
        DetachedCriteria criteriaForCount = CriteriaUtils.clone(criteria);
        if(criteriaForCount == null) {
            logger.warn("复制criteria失败");
            criteria.setProjection(Projections.rowCount());
            long rowCount = DataAccessUtils.longResult(getHibernateTemplate().findByCriteria(criteria));
            criteria.setProjection(null);
            return rowCount;
        }
        criteriaForCount.setProjection(Projections.rowCount());
        return DataAccessUtils.longResult(getHibernateTemplate().findByCriteria(criteriaForCount));
    }

    /**
     * 新增指定的数据对象记录
     * @param entity 要新增的对象
     * @return 新增对象的主键值
     */
    public Serializable save(T entity) {
        return getHibernateTemplate().save(entity);
    }

    public void save(Iterable<T> iterable) {
        save(iterable, DEFAULT_BATCH_INSERT_SIZE);
    }

    public void save(Iterable<T> iterable, int size) {
        int index = 1;
        for (T item : iterable) {
            getHibernateTemplate().save(item);
            if (index % size == 0) {
                getHibernateTemplate().flush();
                getHibernateTemplate().clear();
            }
            index++;
        }
    }

    /**
     * 更新指定的数据对象记录
     * @param entity 更新对象
     */
    public void update(T entity) {
        getHibernateTemplate().update(entity);
    }
    
    public void update(Iterable<T> iterable, int size) {
        int index = 1;
        for (T item : iterable) {
            getHibernateTemplate().update(item);
            if (index % size == 0) {
                getHibernateTemplate().flush();
                getHibernateTemplate().clear();
            }
            index++;
        }
    }

    protected int update(String hql, Object... params) {
        return getHibernateTemplate().bulkUpdate(hql, params);
    }

    public void saveOrUpdate(T entity) {
        getHibernateTemplate().saveOrUpdate(entity);
    }

    /**
     * 根据指定的Hql语句分页查询数据库，若查询语句为复杂的连接查询，
     * 建议使用带total参数的方法以提高统计性能
     * @param queryString hql语句
     * @param params 查询参数
     * @param pageNum 当前页
     * @param pageSize 每页记录数
     * @return 对象集合（分页）
     */
    /*protected Page<T> queryByHql(String queryString, Map<String, ?> params, int pageNum, int pageSize) {
        long total = count(queryString, params);
        return queryByHql(queryString, params, pageNum, pageSize, total);
    }*/

    /**
     * 根据指定的Hql语句分页查询数据库
     * @param queryString hql语句
     * @param params 查询参数
     * @param pageNum 当前页
     * @param pageSize 每页记录数
     * @param total 总记录数
     * @return 对象集合（分页）
     */
    protected Page<T> queryByHql(String queryString, Map<String, ?> params,
                                 int pageNum, int pageSize, long total) {

        if(params == null) {
            params = new HashMap<String, Object>();
        }
        PageRequestWrapper pageRequestWrapper = new PageRequestWrapper(pageNum, pageSize);
        Map<String, Object> paramMap = new HashMap<String, Object>(params);
        List<T> content = getHibernateTemplate().execute(
                new HibernatePaginationHandler<List<T>>(queryString, paramMap, pageRequestWrapper));
        return new PageImpl<T>(content, pageRequestWrapper, total);
    }

    public void delete(T entity) {
        if(entity == null) {
            return;
        }
        getHibernateTemplate().delete(entity);
    }
    
    /**
     * 执行原生SQL语句，支持insert、update、delete等操作
     * 
     * @param sql
     * @return
     * @author jqlin
     */
    @SuppressWarnings({ "unchecked", "rawtypes" })
	protected int excuteBySql(final String sql) {
		return (int) getHibernateTemplate().execute(new HibernateCallback() {
			public Object doInHibernate(Session session) throws HibernateException, SQLException {
				SQLQuery query = session.createSQLQuery(sql);
				return query.executeUpdate();
			}
		});
	}
    
    /**
     * 执行原生SQL语句，支持insert、update、delete等操作
     * 
     * @param sql
     * @param params
     * @return
     * @author jqlin
     */
    @SuppressWarnings({ "unchecked", "rawtypes" })
	protected int excuteBySql(final String sql, final Object... params) {
		return (int) getHibernateTemplate().execute(new HibernateCallback() {
			public Object doInHibernate(Session session) throws HibernateException, SQLException {
				SQLQuery query = session.createSQLQuery(sql);
				if (ArrayUtils.isNotEmpty(params)) {
					for (int i = 0; i < params.length; i++) {
						query.setParameter(i, params[i]);
					}
				}

				return query.executeUpdate();
			}
		});
	}
    
  

    protected abstract Class<T> getEntityClass();

    private class NamedParameterPair {

        private final String[] paramNames;

        private final Object[] values;

        public NamedParameterPair(Map<String, ?> parameters) {
            if(parameters == null) {
                parameters = new HashMap<String, Object>();
            }
            paramNames = new String[parameters.size()];
            values = new Object[parameters.size()];
            int i = 0;
            for (Map.Entry<String, ?> param : parameters.entrySet()) {
                paramNames[i] = param.getKey();
                values[i] = param.getValue();
                i++;
            }
        }

        public String[] getParamNames() {
            return paramNames;
        }

        public Object[] getValues() {
            return values;
        }
    }
}
